package com.atguigu.tingshu.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.HttpClient;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;


@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class WxPayServiceImpl implements WxPayService {

    private final PaymentInfoService paymentInfoService;

    private final RedissonClient redissonClient;

    @Value("${appid}")
    private String appId;

    @Value("${partner}")
    private String partner;

    @Value("${partnerkey}")
    private String partnerkey;

    @Value("${notifyUrl}")
    private String notifyUrl;

    /**
     * 向微信支付系统发起请求，获取支付地址信息
     *
     * @param orderNo 订单号
     * @param money   金额 元
     * @param desc    描述
     * @return 支付地址信息
     */
    @SneakyThrows
    @Override
    public String getPayInfoFromWx(String orderNo, String money, String desc) {

        // 请求的地址
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        // 包装参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", appId);
        paramMap.put("mch_id", partner);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("body", desc);
        paramMap.put("out_trade_no", orderNo);
        paramMap.put("total_fee", new BigDecimal(money).multiply(new BigDecimal(100)).intValue() + "");
        paramMap.put("spbill_create_ip", "192.168.200.1");
        paramMap.put("notify_url", notifyUrl);
        paramMap.put("trade_type", "NATIVE");

        // 转xml数据并生成签名
        String xmlParamsString = WXPayUtil.generateSignedXml(paramMap, partnerkey);

        // 发起请求
        HttpClient httpClient = new HttpClient(url);
        httpClient.setHttps(true);
        httpClient.setXmlParam(xmlParamsString);
        httpClient.post();

        // 解析结果
        String contentXmlString = httpClient.getContent();

        // 将结果转件为map
        Map<String, String> result = WXPayUtil.xmlToMap(contentXmlString);

        // 若正常返回则返回支付地址
        if (result.get("return_code").equals("SUCCESS") && result.get("result_code").equals("SUCCESS")) {
            // 获取用户id
            Long userId = AuthContextHolder.getUserId();
            // 记录本次交易流水
            CompletableFuture.runAsync(() -> {
                savePaymentInfo(orderNo, money, desc, userId);
            });
            return result.get("code_url");
        }
        // 若没有正常返回则返回全部内容
        log.error("订单No:(" + orderNo + ")申请支付地址失败,出现异常,原因为:" + JSONObject.toJSONString(result));
        return null;

    }

    /**
     * 获取订单支付结果
     *
     * @param orderNo 订单号
     * @return 支付结果
     */
    @SneakyThrows
    @Override
    public String getOrderPayResult(String orderNo) {

        // 请求的地址
        String url = "https://api.mch.weixin.qq.com/pay/orderquery";

        // 包装参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", appId);
        paramMap.put("mch_id", partner);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("out_trade_no", orderNo);

        // 转xml数据并生成签名
        String xmlParamsString = WXPayUtil.generateSignedXml(paramMap, partnerkey);

        // 发起请求
        HttpClient httpClient = new HttpClient(url);
        httpClient.setHttps(true);
        httpClient.setXmlParam(xmlParamsString);
        httpClient.post();

        // 解析结果
        String contentXmlString = httpClient.getContent();

        // 将结果转件为map
        Map<String, String> result = WXPayUtil.xmlToMap(contentXmlString);

        if (result.get("return_code").equals("SUCCESS") && result.get("result_code").equals("SUCCESS")) {
            return result.get("trade_state");
        }
        // TODO-在已支付的情况下修改paymentInfo(未支付的数据)

        // TODO-通知订单改状态

        // 若没有正常返回则返回全部内容
        return JSONObject.toJSONString(result);
    }

    /**
     * 根据微信通知的支付结果，修改支付的流水数据
     *
     * @param result 支付结果
     */
    @Override
    public void updatePaymentInfo(Map<String, String> result) {
        // 获取订单号
        String orderNo = result.get("out_trade_no");

        // 获取分布式锁
        RLock lock = redissonClient.getLock("PaymentInfo_Lock_" + orderNo);
        // 每一比通知都要进行校验处理 确保同一订单的支付信息更新操作是线程安全的
        lock.lock();

        try {
            // 查询订单的支付信息
            PaymentInfo paymentInfo = paymentInfoService.getOne(
                    new LambdaQueryWrapper<PaymentInfo>()
                            .eq(PaymentInfo::getOrderNo, orderNo));

            // 获取本次支付的流水号
            String outTradeNo = result.get("transaction_id");

            // 获取状态 如果是未支付
            if (paymentInfo.getPaymentStatus().equals("1401")) {
                //设置支付流水号
                paymentInfo.setOutTradeNo(outTradeNo);
                //设置回调时间为当前时间
                paymentInfo.setCallbackTime(new Date());
                // 设置回调信息
                paymentInfo.setCallbackContent(JSONObject.toJSONString(result));
                // 设置支付状态为已支付
                paymentInfo.setPaymentStatus("1402");
                // 更新后的数据保存数据库
                paymentInfoService.updateById(paymentInfo);
                // TODO-通知订单微服务改订单的状态
                // TODO-通知账户微服务增加余额
            } else {
                // 已支付，获取上一次流水号
                if (!paymentInfo.getOutTradeNo().equals(outTradeNo)) {
                    // 重复支付的订单
                    System.out.println("退款!");
                }
            }
        } catch (Exception e) {
            // TODO-记录到数据库

        } finally {
            lock.unlock();
        }
    }

    /**
     * 保存交易流水信息(全部异步完成)
     *
     * @param orderNo 订单号
     * @param money   金额
     * @param desc    描述
     */
    public void savePaymentInfo(String orderNo, String money, String desc, Long userId) {
        // 初始化
        PaymentInfo paymentInfo = new PaymentInfo();
        // 设置属性
        paymentInfo.setUserId(userId);
        paymentInfo.setPaymentType("1301");
        paymentInfo.setOrderNo(orderNo);
        paymentInfo.setPayWay("1101");
        paymentInfo.setAmount(new BigDecimal(money));
        paymentInfo.setContent(desc);
        paymentInfo.setPaymentStatus("1401");
        // 保存(先删除-新增)
        paymentInfoService.save(paymentInfo);
    }
}
